//
//  UAOracleStatement.m
//  Unidbaccess
//
//  Created by Toplica Tanasković on 12/16/08.
//  Copyright 2008 HOME. All rights reserved.
//

#import "UAOracleStatement.h"
#import "UANumberType.h"

@implementation UAOracleStatement

- (id) initWithStatementData: (void *) aStatementData {
	self = [super initWithStatementData: aStatementData];
	if (self) {
		OCI_SetFetchMode(nativeStatementData, OCI_SFM_SCROLLABLE);
		resultSet = NULL;
	}
	return self;
}

- (BOOL) isColumn: (OCI_Column *) aColumn ofType: (NSUInteger) aType {
	if (OCI_ColumnGetType(aColumn) != aType) {
		return NO;
	} else {
		return YES;
	}
}

- (BOOL) isValueNullAtIndex: (NSUInteger) index {
	if (OCI_IsNull(resultSet, index)) {
		return YES;
	} else {
		return NO;
	}
}

- (BOOL) isValueNullAtColumn: (NSString *) columnName {
	if (OCI_IsNull2(resultSet, [columnName UTF8String])) {
		return YES;
	} else {
		return NO;
	}
}

- (NSNumber *) numericValueOfType: (UANumberType) numberType atColumnIndex: (NSUInteger) index {
	NSNumber * retVal = nil;
	
	if (OCI_GetColumnCount(resultSet) < index) {
		[NSException raise: @"ColumnIndexOutOfBoundsException" format: @"Requested column index is %d, however result set has only %d columns.", index, OCI_GetColumnCount(resultSet)];
	}
	
	if (![self isColumn: OCI_GetColumn(resultSet, index) ofType: OCI_CDT_NUMERIC]) {
		[NSException raise: @"InvalidColumnTypeException" format: @"The type of the column at position %d is not of any numeric type.", index];
	}
	
	if (![self isValueNullAtIndex: index]) {
		switch (numberType) {
			case UANumberBool:
				retVal = [NSNumber numberWithBool: OCI_GetInt(resultSet, index) != 0];
				break;
			case UANumberShort:
				retVal = [NSNumber numberWithShort: OCI_GetShort(resultSet, index)];
				break;
			case UANumberInt:
				retVal = [NSNumber numberWithInt: OCI_GetInt(resultSet, index)];
				break;
			case UANumberLongLong:
				retVal = [NSNumber numberWithLongLong: OCI_GetBigInt(resultSet, index)];
				break;
			case UANumberUnsignedShort:
				retVal = [NSNumber numberWithUnsignedShort: OCI_GetUnsignedShort(resultSet, index)];
				break;
			case UANumberUnsignedInt:
				retVal = [NSNumber numberWithUnsignedInt: OCI_GetUnsignedInt(resultSet, index)];
				break;
			case UANumberUnsignedLongLong:
				retVal = [NSNumber numberWithUnsignedLongLong: OCI_GetUnsignedBigInt(resultSet, index)];
				break;
			case UANumberDouble:
				retVal = [NSNumber numberWithDouble: OCI_GetDouble(resultSet, index)];
				break;
			default:
				retVal = nil;
		}
	}
	
	return retVal;
}

- (NSNumber *) numericValueOfType: (UANumberType) numberType atColumn: (NSString *) columnName {
	NSNumber * retVal = nil;
	if (OCI_GetColumn2(resultSet, [columnName UTF8String]) == NULL) {
		[NSException raise: @"ColumnDoesNotExistException" format: @"Column with name %@ does not exists in record set.", columnName];
	}
	
	if (![self isColumn: OCI_GetColumn2(resultSet, [columnName UTF8String]) ofType: OCI_CDT_NUMERIC]) {
		[NSException raise: @"InvalidColumnTypeException" format: @"Column with name %@ is not of any numeric type.", columnName];
	}
	
	if (![self isValueNullAtColumn: columnName]) {
		switch (numberType) {
			case UANumberBool:
				retVal = [NSNumber numberWithBool: OCI_GetInt2(resultSet, [columnName UTF8String]) != 0];
				break;
			case UANumberShort:
				retVal = [NSNumber numberWithShort: OCI_GetShort2(resultSet, [columnName UTF8String])];
				break;
			case UANumberInt:
				retVal = [NSNumber numberWithInt: OCI_GetInt2(resultSet, [columnName UTF8String])];
				break;
			case UANumberLongLong:
				retVal = [NSNumber numberWithLongLong: OCI_GetBigInt2(resultSet, [columnName UTF8String])];
				break;
			case UANumberUnsignedShort:
				retVal = [NSNumber numberWithUnsignedShort: OCI_GetUnsignedShort2(resultSet, [columnName UTF8String])];
				break;
			case UANumberUnsignedInt:
				retVal = [NSNumber numberWithUnsignedInt: OCI_GetUnsignedInt2(resultSet, [columnName UTF8String])];
				break;
			case UANumberUnsignedLongLong:
				retVal = [NSNumber numberWithUnsignedLongLong: OCI_GetUnsignedBigInt2(resultSet, [columnName UTF8String])];
				break;
			case UANumberDouble:
				retVal = [NSNumber numberWithDouble: OCI_GetDouble2(resultSet, [columnName UTF8String])];
				break;
			default:
				retVal = nil;
		}
	}
	
	return retVal;
}

- (void) dealloc {
	OCI_StatementFree(nativeStatementData);
	
	[super dealloc];
}

- (BOOL) execute {
	BOOL success = OCI_Execute(nativeStatementData);
	resultSet = OCI_GetResultset(nativeStatementData);
	return success;
}

- (BOOL) first {
	return OCI_FetchFirst(resultSet);
}

- (BOOL) previous {
	return OCI_FetchPrev(resultSet);
}

- (BOOL) next {
	return OCI_FetchNext(resultSet);
}

- (BOOL) last {
	return OCI_FetchLast(resultSet);
}

- (NSNumber *) affectedRows {
	return [NSNumber numberWithUnsignedInt: OCI_GetAffectedRows(nativeStatementData)];
}

- (NSString *) query {
	return [NSString stringWithUTF8String: OCI_GetSql(nativeStatementData)];
}

- (NSNumber *) boolValueAtColumnIndex: (NSUInteger) index {
	return [self numericValueOfType: UANumberBool atColumnIndex: index];
}

- (NSNumber *) shortValueAtColumnIndex: (NSUInteger) index {
	return [self numericValueOfType: UANumberShort atColumnIndex: index];
}

- (NSNumber *) intValueAtColumnIndex: (NSUInteger) index {
	return [self numericValueOfType: UANumberInt atColumnIndex: index];
}

- (NSNumber *) longLongValueAtColumnIndex: (NSUInteger) index {
	return [self numericValueOfType: UANumberLongLong atColumnIndex: index];
}

- (NSNumber *) doubleValueAtColumnIndex: (NSUInteger) index {
	return [self numericValueOfType: UANumberDouble atColumnIndex: index];
}

- (NSNumber *) unsignedShortValueAtColumnIndex: (NSUInteger) index {
	return [self numericValueOfType: UANumberUnsignedShort atColumnIndex: index];
}

- (NSNumber *) unsignedIntValueAtColumnIndex: (NSUInteger) index {
	return [self numericValueOfType: UANumberUnsignedInt atColumnIndex: index];
}

- (NSNumber *) unsignedLongLongValueAtIndex: (NSUInteger) index {
	return [self numericValueOfType: UANumberUnsignedLongLong atColumnIndex: index];
}

- (NSString *) stringValueAtColumnIndex: (NSUInteger) index {
	NSString * retVal = nil;

	if (OCI_GetColumnCount(resultSet) < index) {
		[NSException raise: @"ColumnIndexOutOfBoundsException" format: @"Requested column index is %d, however result set has only %d columns.", index, OCI_GetColumnCount(resultSet)];
	}
	
	if (![self isColumn: OCI_GetColumn(resultSet, index) ofType: OCI_CDT_TEXT]) {
		[NSException raise: @"InvalidColumnTypeException" format: @"The type of the column at position %d is not of any string type.", index];
	}
	
	if (![self isValueNullAtIndex: index]) {
		retVal = [NSString stringWithUTF8String: OCI_GetString(resultSet, index)];
	}
	
	return retVal;
}

- (NSDate *) dateValueAtColumnIndex: (NSUInteger) index {
	NSDate * retVal = nil;
	
	if (OCI_GetColumnCount(resultSet) < index) {
		[NSException raise: @"ColumnIndexOutOfBoundsException" format: @"Required column index is %d, however result set has only %d columns.", index, OCI_GetColumnCount(resultSet)];
	}
	
	if (![self isColumn: OCI_GetColumn(resultSet, index) ofType: OCI_CDT_DATETIME]) {
		[NSException raise: @"InvalidColumnTypeException" format: @"The type of the column at position %d is not of any date/time type.", index];
	}
	
	if (![self isValueNullAtIndex: index]) {
		OCI_Date * d = OCI_GetDate(resultSet, index);
		time_t t;
		if (OCI_DateToCTime(d, NULL, &t)) {
			retVal = [NSDate dateWithTimeIntervalSince1970: t];
		}
	}
	
	return retVal;
}

- (NSDate *) timestampValueAtColumnIndex: (NSInteger) index {
	NSDate *		retVal = nil;
	OCI_Column *	column = NULL;
	
	column = OCI_GetColumn(resultSet, index);
	
	if (column == NULL) {
		[NSException raise: @"ColumnIndexOutOfBoundsException" format: @"Required column index is %d, however result set has only %d columns.", index, OCI_GetColumnCount(resultSet)];
	}
	
	if (![self isColumn: column ofType: OCI_CDT_TIMESTAMP]) {
		[NSException raise: @"InvalidColumnTypeException" format: @"The type of the column at position %d is not a timestamp type.", index];
	}
	
	if (![self isValueNullAtIndex: index]) {
		OCI_Timestamp * t = OCI_GetTimestamp(resultSet, index);
		int precision = OCI_ColumnGetFractionnalPrecision(column);
		int year, month, day, hour, min, sec, fsec;
		time_t ctime;
		OCI_TimestampGetDateTime(t, &year, &month, &day, &hour, &min, &sec, &fsec);
		OCI_TimestampToCTime(t, NULL, &ctime);
		NSTimeInterval timestamp = ((NSTimeInterval)ctime) + (((NSTimeInterval) fsec)/((NSTimeInterval) precision));
		retVal = [NSDate dateWithTimeIntervalSince1970: timestamp];
	}

	return retVal;
}

- (NSNumber *) boolValueAtColumn: (NSString *) columnName {
	return [self numericValueOfType: UANumberBool atColumn: columnName];
}

- (NSNumber *) shortValueAtColumn: (NSString *) columnName {
	return [self numericValueOfType: UANumberShort atColumn: columnName];
}

- (NSNumber *) intValueAtColumn: (NSString *) columnName {
	return [self numericValueOfType: UANumberInt atColumn: columnName];
}

- (NSNumber *) longLongValueAtColumn: (NSString *) columnName {
	return [self numericValueOfType: UANumberLongLong atColumn: columnName];
}

- (NSNumber *) doubleValueAtColumn: (NSString *) columnName {
	return [self numericValueOfType: UANumberDouble atColumn: columnName];
}

- (NSNumber *) unsignedShortValueAtColumn: (NSString *) columnName {
	return [self numericValueOfType: UANumberUnsignedShort atColumn: columnName];
}

- (NSNumber *) unsignedIntValueAtColumn: (NSString *) columnName {
	return [self numericValueOfType: UANumberUnsignedInt atColumn: columnName];
}

- (NSNumber *) unsignedLongLongValueAtColumn: (NSString *) columnName {
	return [self numericValueOfType: UANumberUnsignedLongLong atColumn: columnName];
}

- (NSString *) stringValueAtColumn: (NSString *) columnName {
	NSString * retVal = nil;

	if (OCI_GetColumn2(resultSet, [columnName UTF8String]) == NULL) {
		[NSException raise: @"ColumnDoesNotExistException" format: @"Column with name %@ does not exists in record set.", columnName];
	}
	
	if (![self isColumn: OCI_GetColumn2(resultSet, [columnName UTF8String]) ofType: OCI_CDT_TEXT]) {
		[NSException raise: @"InvalidColumnTypeException" format: @"Column with name %@ is not of any string type.", columnName];
	}
	
	if (![self isValueNullAtColumn: columnName]) {
		retVal = [NSString stringWithUTF8String: OCI_GetString2(resultSet, [columnName UTF8String])];
	}
	
	return retVal;
}

- (NSDate *) dateValueAtColumn: (NSString *) columnName {
	NSDate * retVal = nil;
	
	if (OCI_GetColumn2(resultSet, [columnName UTF8String]) == NULL) {
		[NSException raise: @"ColumnDoesNotExistException" format: @"Column with name %@ does not exist in record set.", columnName];
	}
	
	if (![self isColumn: OCI_GetColumn2(resultSet, [columnName UTF8String]) ofType: OCI_CDT_DATETIME]) {
		[NSException raise: @"InvalidColumnTypeException" format: @"The type of the column with name %@ is not of any date/time type.", index];
	}

	if (![self isValueNullAtColumn: columnName]) {
		OCI_Date * d = OCI_GetDate2(resultSet, [columnName UTF8String]);
		time_t t;
		if (OCI_DateToCTime(d, NULL, &t)) {
			retVal = [NSDate dateWithTimeIntervalSince1970: t];
		}
	}

	return retVal;
}

- (NSDate *) timestampValueAtColumn: (NSString *) columnName {
	NSDate *		retVal = nil;
	OCI_Column *	column = NULL;
	
	column = OCI_GetColumn2(resultSet, [columnName UTF8String]);
	
	if (column == NULL) {
		[NSException raise: @"ColumnDoesNotExistException" format: @"Column with name %@ does not exist in record set.", columnName];
	}
	
	if (![self isColumn: column ofType: OCI_CDT_TIMESTAMP]) {
		[NSException raise: @"InvalidColumnTypeException" format: @"The type of the column with name %@ is not a timestamp type.", columnName];
	}
	
	if (![self isValueNullAtColumn: columnName]) {
		OCI_Timestamp * t = OCI_GetTimestamp2(resultSet, [columnName UTF8String]);
		int precision = OCI_ColumnGetFractionnalPrecision(column);
		int year, month, day, hour, min, sec, fsec;
		time_t ctime;
		OCI_TimestampGetDateTime(t, &year, &month, &day, &hour, &min, &sec, &fsec);
		OCI_TimestampToCTime(t, NULL, &ctime);
		NSTimeInterval timestamp = ((NSTimeInterval)ctime) + (((NSTimeInterval) fsec)/((NSTimeInterval) precision));
		retVal = [NSDate dateWithTimeIntervalSince1970: timestamp];
	}

	return retVal;
}

- (BOOL) bindBool: (NSNumber *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindBool: value forName: name];	
}

- (BOOL) bindShort: (NSNumber *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindShort: value forName: name];
}

- (BOOL) bindInt: (NSNumber *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindInt: value forName: name];
}

- (BOOL) bindLongLong: (NSNumber *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindLongLong: value forName: name];	
}

- (BOOL) bindDouble: (NSNumber *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindDouble: value forName: name];
}

- (BOOL) bindUnsignedShort: (NSNumber *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindUnsignedShort: value forName: name];
}

- (BOOL) bindUnsignedInt: (NSNumber *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindUnsignedInt: value forName: name];
}

- (BOOL) bindUnsignedLongLong: (NSNumber *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindUnsignedLongLong: value forName: name];
}

- (BOOL) bindString: (NSString *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindString: value forName: name];
}

- (BOOL) bindDate: (NSDate *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindDate: value forName: name];
}

- (BOOL) bindTimestamp: (NSDate *) value atIndex: (NSUInteger) index {
	if (index < 1) {
		[NSException raise: @"IndexOutOfBoundsException" format: @"When using positional binding index starts with 1."];
	}
	NSString * name = [NSString stringWithFormat: @":%d", index];
	return [self bindTimestamp: value forName: name];
}

- (BOOL) bindBool: (NSNumber *) value forName: (NSString *) name {
	BOOL retVal = NO;
	int data;
	if (value != nil) {
		data = [value intValue];
		retVal = OCI_BindInt(nativeStatementData, [name UTF8String], &data);
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	return retVal;
}

- (BOOL) bindShort: (NSNumber *) value forName: (NSString *) name {
	BOOL retVal = NO;
	short data;
	if (value != nil) {
		data = [value shortValue];
		retVal = OCI_BindShort(nativeStatementData, [name UTF8String], &data);
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	return retVal;
}

- (BOOL) bindInt: (NSNumber *) value forName: (NSString *) name {
	BOOL retVal = NO;
	int data;
	if (value != nil) {
		data = [value intValue];
		retVal = OCI_BindInt(nativeStatementData, [name UTF8String], &data);
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	return retVal;
}

- (BOOL) bindLongLong: (NSNumber *) value forName: (NSString *) name {
	BOOL retVal = NO;
	long long data;
	if (value != nil) {
		data = [value longLongValue];
		retVal = OCI_BindBigInt(nativeStatementData, [name UTF8String], &data);
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	return retVal;
}

- (BOOL) bindDouble: (NSNumber *) value forName: (NSString *) name {
	BOOL retVal = NO;
	double data;
	if (value != nil) {
		data = [value doubleValue];
		retVal = OCI_BindDouble(nativeStatementData, [name UTF8String], &data);
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	return retVal;
}

- (BOOL) bindUnsignedShort: (NSNumber *) value forName: (NSString *) name {
	BOOL retVal = NO;
	unsigned short data;
	if (value != nil) {
		data = [value unsignedShortValue];
		retVal = OCI_BindUnsignedShort(nativeStatementData, [name UTF8String], &data);
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	return retVal;
}

- (BOOL) bindUnsignedInt: (NSNumber *) value forName: (NSString *) name {
	BOOL retVal = NO;
	unsigned int data;
	if (value != nil) {
		data = [value unsignedIntValue];
		retVal = OCI_BindUnsignedInt(nativeStatementData, [name UTF8String], &data);
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	return retVal;
}

- (BOOL) bindUnsignedLongLong: (NSNumber *) value forName: (NSString *) name {
	BOOL retVal = NO;
	unsigned long long data;
	if (value != nil) {
		data = [value intValue];
		retVal = OCI_BindUnsignedBigInt(nativeStatementData, [name UTF8String], &data);
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	return retVal;
}

- (BOOL) bindString: (NSString *) value forName: (NSString *) name {
	BOOL retVal = NO;
	char * data;
	if (value != nil) {
		data = [value UTF8String];
		retVal = OCI_BindString(nativeStatementData, [name UTF8String], data, strlen(data));
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	
	return retVal;
}

- (BOOL) bindDate: (NSDate *) value forName: (NSString *) name {
	BOOL retVal = NO;
	OCI_Date * data;
	if (value != nil) {
		data = OCI_DateCreate(NULL);
		if (data == NULL) {
			[NSException raise: @"OCIDateCreationException" format: @"An unexpected failiure occured while trying to create OCI_Date instance."];
		}
		if (!OCI_DateFromCTime(data, NULL, [value timeIntervalSince1970])) {
			[NSException raise: @"OCIDateCreationException" format: @"An unexpected failure occured while trying to set OCI_Date instance with provided value."];
		}
		retVal = OCI_BindDate(nativeStatementData, [name UTF8String], data);
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	return retVal;
}

- (BOOL) bindTimestamp: (NSDate *) value forName: (NSString *) name {
	BOOL retVal = NO;
	OCI_Timestamp * data;
	if (value != nil) {
		data = OCI_TimestampCreate(NULL, OCI_TIMESTAMP);
		if (data == NULL) {
			[NSException raise: @"OCITimestampCreationException" format: @"An unexpected failiure occured while trying to create OCI_Timestamp instance."];
		}
		if (!OCI_TimestampFromCTime(data, NULL, [value timeIntervalSince1970])) {
			[NSException raise: @"OCITimestampCreationException" format: @"An unexpected failure occured while trying to set OCI_Timestamp instance with provided value."];
		}
		retVal = OCI_BindTimestamp(nativeStatementData, [name UTF8String], data);
	} else {
		retVal = OCI_SetNull2(nativeStatementData, [name UTF8String]);
	}
	return retVal;
}
@end
